Zohaib Zahid
Project #2 – Data Exploration and Design
Due: 2/22/2021
Data
The dataset chosen for this assignment is from Kaggle: https://www.kaggle.com/kenhuang41/nba-basic-game-data-by-player
This dataset features individual player data from all games they played in from start of 1996-97 season to December 31, 2020.
Loading in the library
library(dplyr)
library(ggplot2)
Read the file
games <- read.csv(file = 'games.csv')
Check the top of the data:
head(games)
- GAMES_ID - Unique Game ID (Nominal)
- TEAM - Player’s team (Nominal)
- OPPT - Opponent’s team (Nominal)
- TEAM_SCORE - Player’s Team Score (Interval)
- OPPY_SCORE - Oppoenet’s Team Score (Interval)
- RESULT - Win/Loss (Ordinal)
- SCORE_DIFF - Team Score - Opponent Team (Interval)
- PLAYER - Player Name (Nominal)
- MP - Minutes played (Interval)
- FG - Field Goals Made (Interval)
- FGA - Field Goals Attempted (Interval)
- FG3 - 3 Pointers Made (Interval)
- FG3A - 3 Pointers Attempted (Interval)
- FT - Free Throws Made (Interval)
- FTA - Free Throws Attempted (Interval)
- ORB - Offensive Rebounds (Interval)
- DRB - Defensive Rebounds (Interval)
- TRB - Total Rebounds (Interval)
- AST - Assists (Interval)
- STL - Steals (Interval)
- BLK - Blocks (Interval)
- TOV - Turnovers (Interval)
- PF - Personal Fouls (Interval)
- PLUS_MINUS - Individual Plus Minus (Interval)
- PTS - Total Points (Interval)
- TOTAL_MINS - Total Minutes (Interval)
- STARTER - Started or Not (Ordinal)
Run a quick analysis on the data provided:
summary(games)
Notice that this dataset has 743423 entries. This represents every player, in every game played since 1996-97 season, upto December 31, 2020. When taking a closer look at the score difference, it is pretty symmetrical almost with the mean being zero, because the data also include data of those players on the losing team, therefore the score difference shown for that player is negative.
For some players this season is mid career, for others it’s the start. However, for this analysis all that matters is the time period of this dataset.
To make this data a bit more useful to us, lets add a column just for year the game was played, which can be processed as a subset of the game_ID. Although the year does not exactly correspond to the season since seasons go across 2 years usually, this will still help analyze how over the years players have changed.
games <- mutate(games, year = substr(GAME_ID, 12, 15))
Now, Lets take a look at the score distribution of the winning teams in this dataset:
by_game <- group_by(games,GAME_ID)
by_winning_game_score <- group_by(by_game,TEAM_SCORE, .add=TRUE) %>%
filter(RESULT=="W")
Winning_Games_dist <- summarise(by_winning_game_score)
hist(Winning_Games_dist$TEAM_SCORE)
abline(v = mean(Winning_Games_dist$TEAM_SCORE), col = "blue", lwd = 2)
boxplot(Winning_Games_dist$TEAM_SCORE)
summary(Winning_Games_dist$TEAM_SCORE)
Lets take a look at the score distribution of the losing teams in this dataset:
by_game <- group_by(games,GAME_ID)
by_losing_game_score <- group_by(by_game,TEAM_SCORE, .add=TRUE) %>%
filter(RESULT=="L")
Losing_Games_dist <- summarise(by_losing_game_score)
hist(Losing_Games_dist$TEAM_SCORE)
abline(v = mean(Losing_Games_dist$TEAM_SCORE), col = "blue", lwd = 2)
boxplot(Losing_Games_dist$TEAM_SCORE)
summary(Losing_Games_dist$TEAM_SCORE)
Analysis
Now that there is a little better understanding of the data, lets take a look at five analytical questions at hand:
- Players with the most points scored since 1996-97 season?
- Players with the most games over 40 points?
- Who has the best field goal ratios in their career?*
- Player with the best overall stats in their career?*
- Which team did LeBron play the best on?*
- the questions selected for the visualization
Question 1 - Players with the most points scored since 1996-97 season?
by_player <- group_by(games,PLAYER)
most_points_scored <- summarise(by_player, points = sum(PTS)) %>%
arrange(desc(points)) %>%
top_n(5)
most_points_scored
Here is a list of the top 5 players with the highest points scored since 1996-97 season.
Question 2 - Players with the most games over 40 points?
most_games_over_40pts <- group_by(games,PLAYER) %>%
filter(PTS>40) %>%
count(PLAYER) %>%
arrange(desc(n)) %>%
ungroup(PLAYER) %>%
top_n(5)
most_games_over_40pts
Here is a list of the top 5 players with the most games with atleast 40 points since 1996-97 season.
Now that we have a few of what I would consider the top players in the NBA, according to total points scored and most games with atleast 40 pts. Lets analyze a few stats from these individuals to try and help narrow down and determine who is the best of these player in the NBA since 1996-97 season.
Question 3 - Who has the best field goal ratios in their career?
fgr <- mutate(games, FGR = FG/FGA, FGR3PT = FG3/FG3A, FGRFT = FT/FTA) %>%
filter(PLAYER == most_games_over_40pts$PLAYER | PLAYER == most_points_scored$PLAYER)
best_fgr <- group_by(fgr,PLAYER) %>%
summarise(avgfgr = mean(FGR, na.rm = TRUE)) %>%
arrange(desc(avgfgr))
best_3ptr <- group_by(fgr,PLAYER) %>%
summarise(avg3ptr = mean(FGR3PT, na.rm = TRUE)) %>%
arrange(desc(avg3ptr))
best_ftr <- group_by(fgr,PLAYER) %>%
summarise(avgftr = mean(FGRFT, na.rm = TRUE)) %>%
arrange(desc(avgftr))
merged_fgr <- merge(best_fgr,best_3ptr,by.x="PLAYER") %>%
merge(best_ftr,by.x="PLAYER")
merged_fgr
See Viz 3.1 below- This visualization is represented by a bar plot, in descending order. The reason for this type of chart for this data was to show the “rank” of these eight (8) players. Looking at both the table and chart will help understand the data better. The other part of this visualization that helps is that it is ranked in decsending order to give the viewer less items to look at all at once, as comparing bar heights can be deceiving sometimes.
See Viz 3.2 below - This visualization is represented by a lollipop chart. The reason for this type of chart for this data was to show the same data from a somewhat different view “rank” of these eight (8) players. However, the data being represented here different and it is using the average 3 point ratio (avg3ptr). Similarly, the chart is in descending order to give the viewer less items to look at all at once.
See Viz 3.3 below - This visualization is represented by a circle plot with 3 axes, one for each column/feature. The reason for this type of chart for this data was to show a comparison between players. For the purpose and proof of concept, used it to represent only 2 players(Lebron James and Kobe Bryant). There would have been 28 combinations for any 2 players, and would best work with an interactive view of some sort to select player 1 and player 2 for comparison. The purpose of this visualization would be to easily be able to compare stats of 2 players versus having to look at multiple charts, and piecing together information.
Extra: In addition, to the career stats, sometimes its meaningful to look at a few per year stats as well. Below is the Field goal ratio per year for these players, along with their points per game per year stats. This will help with making sure the players that are scoring higher points are not doing so at the sacrifice of their field goal percentage, and not have it play into the idea of more shots taken the higher the score.
by_player <- filter(fgr) %>%
group_by(PLAYER, year)
ppg_per_yr <- summarise(by_player, points = mean(PTS, na.rm = TRUE)) %>%
arrange(year)
ppg_per_yr
# plot
ggplot(data = ppg_per_yr, aes(x=ppg_per_yr$year, y=ppg_per_yr$points, group = ppg_per_yr$PLAYER, color =ppg_per_yr$PLAYER)) + geom_line() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
As seen in this visualization above, the top 3 players of the 8 that are being examined are James Harden, Lebron James, and Kevin Durant in terms of most points per game per year.
by_player <- filter(fgr) %>%
group_by(PLAYER, year)
best_fgr_per_yr <- summarise(by_player, FGR_per_yr = mean(FGR, na.rm = TRUE)) %>%
arrange(year)
best_fgr_per_yr
# plot
ggplot(data = best_fgr_per_yr, aes(x=best_fgr_per_yr$year, y=best_fgr_per_yr$FGR_per_yr, group = best_fgr_per_yr$PLAYER, color =best_fgr_per_yr$PLAYER)) + geom_line() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
Now in the visualizaiton above, lets take a closer look at James Harden, Lebron James, and Kevin Durant in terms of their field goal percentage per year. Notice that James Harden has a lower ratio than both Lebron James and Kevin Durant, leading us to believe he has higher points per game only because of how many shots he took in total.
Question 4 - Player with the best overall stats in their career?
stats <- filter(fgr)
best_stats <- group_by(stats,PLAYER) %>%
summarise(avgTRB = mean(TRB, na.rm = TRUE), avgAST = mean(AST, na.rm = TRUE), avgSTL = mean(STL, na.rm = TRUE), avgBLK = mean(BLK, na.rm = TRUE)) %>%
mutate(total = avgTRB+avgAST+avgSTL+avgBLK) %>%
arrange(desc(total))
best_stats
by_player <- filter(fgr) %>%
group_by(PLAYER, year)
best_stats_per_year <- summarise(by_player, avgTRB = mean(TRB, na.rm = TRUE), avgAST = mean(AST, na.rm = TRUE), avgSTL = mean(STL, na.rm = TRUE), avgBLK = mean(BLK, na.rm = TRUE)) %>%
mutate(total = avgTRB+avgAST+avgSTL+avgBLK) %>%
arrange(desc(total))
best_stats_per_year
ggplot(data = best_stats_per_year, aes(x=best_stats_per_year$year, y=best_stats_per_year$total, group = best_stats_per_year$PLAYER, color =best_stats_per_year$PLAYER)) + geom_line() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
See Viz 4.1 above- This visualization is represented by a multiple line plot. This visualization allows the user to view 3 variables at the same time. The visualization is able to show the total stats(y axis), the year(x axis), and the player (colored line).The y axis is calculated by adding together the average total rebounds per game, average assist per game, average steal per game, and average block per game - all for a given year. This is also just a simplification of the stats, as in the real game there are weights associated with these stats, where rebounds and assists are more heavily weighed than blocks and steals.
See Viz 4.2 below- This visualization is represented by a multiple marks. This visualization also allows the user to view 3 variables at the same time. However, in this visualization it shows the separate stats per person and not per year. The average stat score is on the y axis, the stat on the x axis, and each player is represented by a different mark.The y axis represent the average total rebounds per game, average assist per game, average steal per game, and average block per game - in a players whole career.
See Viz 4.3 below- This visualization is represented by stacked bar chart. This visualization also allows the user to view multiple variables at once as well. In this visualization, the stacked bar is able to show the breakdown of the total stat. The total stat score is on the y axis, the player is on the x axis, and each stat is represented by a different shading/texture.These are also representing whoel career stats versus the yearly stats seen in Viz 4_1.
To continue the conversation of who is best, initially in Viz 4_1 it can be seen that Dirk Nowitzki and Tim Duncan were on top, until Lebron James started his career in 2003, and they started on what looks to be a steady decline until retirement. Lebron James since starting in 2003, had 12 out of 18 years of being on the top of this chart, and the other 6 years he was second on this chart. Lebron James has the highest overall stat total from the 8 players seen in Viz 4_3.
Safe to say that statistically, Lebron James is the best overall player in the NBA according to this dataset that covers all games from 1996-97 season to December 31, 2020.
Question 5 - Which team did LeBron play the best on?
by_player <- filter(games, PLAYER=="LeBron James") %>%
group_by(TEAM,year)
Lebron_most_points_per_year <- summarise(by_player, points = sum(PTS)) %>%
arrange(year)
Lebron_most_points_per_year
# plot
ggplot(data = Lebron_most_points_per_year, aes(x=Lebron_most_points_per_year$TEAM, y=Lebron_most_points_per_year$points, group = Lebron_most_points_per_year$TEAM, color=Lebron_most_points_per_year$TEAM)) + geom_boxplot() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
See Viz 5.1 above- This visualization is represented by a multiple box plots. This visualization allows the spread of scores. The visualization is able to show the total points per year(y axis), the team(x axis). This chart was chosen to not only show the Lebron’s average points scored per year, but to show the range of scores he has scored for a particular team. Notice that the box plot for Lakers is small, that is ultimately due to the limited number of years he has been with that franchise. On average, Lebron had the most points scored per year on the Cleveland Cavaliers.
Lebron_avg_points_per_year <-summarise(by_player, avgMinsPlayed = mean(MP, na.rm=TRUE), points = mean(PTS, na.rm=TRUE)) %>%
arrange(year)
Lebron_avg_points_per_year
# plot
ggplot(data = Lebron_avg_points_per_year, aes(x=Lebron_avg_points_per_year$year, y=Lebron_avg_points_per_year$points, group = Lebron_avg_points_per_year$TEAM, color=Lebron_avg_points_per_year$TEAM)) + geom_point() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
See Viz 5.2 above- This visualization is represented by a scatter plot. This visualization allows to spread of scores across multiple years. The visualization is able to show the average points per game per year(y axis), the year(x axis). This chart was chosen to to show how Lebron scored on average in any given year. Notice that only his first 2 years in the NBA did he have a 22.5 and lower average game score (which is still very impressive). Also that his top seasons were with the Cleveland Cavaliers.
Lebron_team_summary <- filter(games, PLAYER=="LeBron James") %>%
group_by(TEAM) %>%
count()
Lebron_team_summary
LeBron_Points_Summary <- ungroup(Lebron_most_points_per_year) %>%
group_by(TEAM) %>%
summarize(TotalPts = sum(points))
LeBron_Points_Summary
See Viz 5.3 below- This visualization is represented by pie chart. This visualization also allows the user to view relative share of games played and total points scored with a certain team. Notice here that the games played share and the points scored for Lebron are very highly correlated. which means he has been really consistent during his time with each team.
In conclusion to this question, the team that he performed his best so far is with the Cleveland Cavaliers.
LS0tCnRpdGxlOiAiTkJBIEdhbWUgU3RhdHMgU2luY2UgdGhlIDE5OTYtOTcgU2Vhc29uIgpvdXRwdXQ6CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgd29yZF9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgpab2hhaWIgWmFoaWQgIApQcm9qZWN0ICMyIOKAkyBEYXRhIEV4cGxvcmF0aW9uIGFuZCBEZXNpZ24gIApEdWU6IDIvMjIvMjAyMSAgCgo8aDI+RGF0YTwvaDI+CgpUaGUgZGF0YXNldCBjaG9zZW4gZm9yIHRoaXMgYXNzaWdubWVudCBpcyBmcm9tIEthZ2dsZTogCmh0dHBzOi8vd3d3LmthZ2dsZS5jb20va2VuaHVhbmc0MS9uYmEtYmFzaWMtZ2FtZS1kYXRhLWJ5LXBsYXllcgoKVGhpcyBkYXRhc2V0IGZlYXR1cmVzIGluZGl2aWR1YWwgcGxheWVyIGRhdGEgZnJvbSBhbGwgZ2FtZXMgdGhleSBwbGF5ZWQgaW4gZnJvbSBzdGFydCBvZiAxOTk2LTk3IHNlYXNvbiB0byBEZWNlbWJlciAzMSwgMjAyMC4gCgpMb2FkaW5nIGluIHRoZSBsaWJyYXJ5CmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKUmVhZCB0aGUgZmlsZQpgYGB7cn0KZ2FtZXMgPC0gcmVhZC5jc3YoZmlsZSA9ICdnYW1lcy5jc3YnKQpgYGAKCkNoZWNrIHRoZSB0b3Agb2YgdGhlIGRhdGE6CmBgYHtyfQpoZWFkKGdhbWVzKQpgYGAKKiBHQU1FU19JRCAtIFVuaXF1ZSBHYW1lIElEIChOb21pbmFsKSAgCiogVEVBTSAtIFBsYXllcidzIHRlYW0gKE5vbWluYWwpICAKKiBPUFBUIC0gT3Bwb25lbnQncyB0ZWFtIChOb21pbmFsKSAgCiogVEVBTV9TQ09SRSAtIFBsYXllcidzIFRlYW0gU2NvcmUgKEludGVydmFsKSAgCiogT1BQWV9TQ09SRSAtIE9wcG9lbmV0J3MgVGVhbSBTY29yZSAoSW50ZXJ2YWwpICAKKiBSRVNVTFQgLSBXaW4vTG9zcyAoT3JkaW5hbCkgIAoqIFNDT1JFX0RJRkYgLSBUZWFtIFNjb3JlIC0gT3Bwb25lbnQgVGVhbSAoSW50ZXJ2YWwpICAKKiBQTEFZRVIgLSBQbGF5ZXIgTmFtZSAoTm9taW5hbCkgIAoqIE1QIC0gTWludXRlcyBwbGF5ZWQgKEludGVydmFsKSAgCiogRkcgLSBGaWVsZCBHb2FscyBNYWRlIChJbnRlcnZhbCkgIAoqIEZHQSAtIEZpZWxkIEdvYWxzIEF0dGVtcHRlZCAoSW50ZXJ2YWwpICAKKiBGRzMgLSAzIFBvaW50ZXJzIE1hZGUgKEludGVydmFsKSAgCiogRkczQSAtIDMgUG9pbnRlcnMgQXR0ZW1wdGVkIChJbnRlcnZhbCkgIAoqIEZUIC0gRnJlZSBUaHJvd3MgTWFkZSAoSW50ZXJ2YWwpICAKKiBGVEEgLSBGcmVlIFRocm93cyBBdHRlbXB0ZWQgKEludGVydmFsKSAgCiogT1JCIC0gT2ZmZW5zaXZlIFJlYm91bmRzIChJbnRlcnZhbCkgIAoqIERSQiAtIERlZmVuc2l2ZSBSZWJvdW5kcyAoSW50ZXJ2YWwpICAKKiBUUkIgLSBUb3RhbCBSZWJvdW5kcyAoSW50ZXJ2YWwpICAKKiBBU1QgLSBBc3Npc3RzIChJbnRlcnZhbCkgIAoqIFNUTCAtIFN0ZWFscyAoSW50ZXJ2YWwpICAKKiBCTEsgLSBCbG9ja3MgKEludGVydmFsKSAgCiogVE9WIC0gVHVybm92ZXJzIChJbnRlcnZhbCkgIAoqIFBGIC0gUGVyc29uYWwgRm91bHMgKEludGVydmFsKSAgCiogUExVU19NSU5VUyAtIEluZGl2aWR1YWwgUGx1cyBNaW51cyAoSW50ZXJ2YWwpICAKKiBQVFMgLSBUb3RhbCBQb2ludHMgKEludGVydmFsKSAgCiogVE9UQUxfTUlOUyAtIFRvdGFsIE1pbnV0ZXMgKEludGVydmFsKSAgCiogU1RBUlRFUiAtIFN0YXJ0ZWQgb3IgTm90IChPcmRpbmFsKSAgCgpSdW4gYSBxdWljayBhbmFseXNpcyBvbiB0aGUgZGF0YSBwcm92aWRlZDoKYGBge3J9CnN1bW1hcnkoZ2FtZXMpCmBgYAoKTm90aWNlIHRoYXQgdGhpcyBkYXRhc2V0IGhhcyA3NDM0MjMgZW50cmllcy4gVGhpcyByZXByZXNlbnRzIGV2ZXJ5IHBsYXllciwgaW4gZXZlcnkgZ2FtZSBwbGF5ZWQgc2luY2UgMTk5Ni05NyBzZWFzb24sIHVwdG8gRGVjZW1iZXIgMzEsIDIwMjAuIFdoZW4gdGFraW5nIGEgY2xvc2VyIGxvb2sgYXQgdGhlIHNjb3JlIGRpZmZlcmVuY2UsIGl0IGlzIHByZXR0eSBzeW1tZXRyaWNhbCBhbG1vc3Qgd2l0aCB0aGUgbWVhbiBiZWluZyB6ZXJvLCBiZWNhdXNlIHRoZSBkYXRhIGFsc28gaW5jbHVkZSBkYXRhIG9mIHRob3NlIHBsYXllcnMgb24gdGhlIGxvc2luZyB0ZWFtLCB0aGVyZWZvcmUgdGhlIHNjb3JlIGRpZmZlcmVuY2Ugc2hvd24gZm9yIHRoYXQgcGxheWVyIGlzIG5lZ2F0aXZlLiAKCkZvciBzb21lIHBsYXllcnMgdGhpcyBzZWFzb24gaXMgbWlkIGNhcmVlciwgZm9yIG90aGVycyBpdCdzIHRoZSBzdGFydC4gSG93ZXZlciwgZm9yIHRoaXMgYW5hbHlzaXMgYWxsIHRoYXQgbWF0dGVycyBpcyB0aGUgdGltZSBwZXJpb2Qgb2YgdGhpcyBkYXRhc2V0LgoKVG8gbWFrZSB0aGlzIGRhdGEgYSBiaXQgbW9yZSB1c2VmdWwgdG8gdXMsIGxldHMgYWRkIGEgY29sdW1uIGp1c3QgZm9yIHllYXIgdGhlIGdhbWUgd2FzIHBsYXllZCwgd2hpY2ggY2FuIGJlIHByb2Nlc3NlZCBhcyBhIHN1YnNldCBvZiB0aGUgZ2FtZV9JRC4gQWx0aG91Z2ggdGhlIHllYXIgZG9lcyBub3QgZXhhY3RseSBjb3JyZXNwb25kIHRvIHRoZSBzZWFzb24gc2luY2Ugc2Vhc29ucyBnbyBhY3Jvc3MgMiB5ZWFycyB1c3VhbGx5LCB0aGlzIHdpbGwgc3RpbGwgaGVscCBhbmFseXplIGhvdyBvdmVyIHRoZSB5ZWFycyBwbGF5ZXJzIGhhdmUgY2hhbmdlZC4gCmBgYHtyfQpnYW1lcyA8LSBtdXRhdGUoZ2FtZXMsIHllYXIgPSBzdWJzdHIoR0FNRV9JRCwgMTIsIDE1KSkKYGBgCgpOb3csIExldHMgdGFrZSBhIGxvb2sgYXQgdGhlIHNjb3JlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgd2lubmluZyB0ZWFtcyBpbiB0aGlzIGRhdGFzZXQ6IApgYGB7cn0KYnlfZ2FtZSA8LSBncm91cF9ieShnYW1lcyxHQU1FX0lEKQpieV93aW5uaW5nX2dhbWVfc2NvcmUgPC0gZ3JvdXBfYnkoYnlfZ2FtZSxURUFNX1NDT1JFLCAuYWRkPVRSVUUpICU+JQogIGZpbHRlcihSRVNVTFQ9PSJXIikKV2lubmluZ19HYW1lc19kaXN0IDwtIHN1bW1hcmlzZShieV93aW5uaW5nX2dhbWVfc2NvcmUpCmhpc3QoV2lubmluZ19HYW1lc19kaXN0JFRFQU1fU0NPUkUpCmFibGluZSh2ID0gbWVhbihXaW5uaW5nX0dhbWVzX2Rpc3QkVEVBTV9TQ09SRSksIGNvbCA9ICJibHVlIiwgbHdkID0gMikKYm94cGxvdChXaW5uaW5nX0dhbWVzX2Rpc3QkVEVBTV9TQ09SRSkKc3VtbWFyeShXaW5uaW5nX0dhbWVzX2Rpc3QkVEVBTV9TQ09SRSkKYGBgCkxldHMgdGFrZSBhIGxvb2sgYXQgdGhlIHNjb3JlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbG9zaW5nIHRlYW1zIGluIHRoaXMgZGF0YXNldDogCmBgYHtyfQpieV9nYW1lIDwtIGdyb3VwX2J5KGdhbWVzLEdBTUVfSUQpCmJ5X2xvc2luZ19nYW1lX3Njb3JlIDwtIGdyb3VwX2J5KGJ5X2dhbWUsVEVBTV9TQ09SRSwgLmFkZD1UUlVFKSAlPiUKICBmaWx0ZXIoUkVTVUxUPT0iTCIpCkxvc2luZ19HYW1lc19kaXN0IDwtIHN1bW1hcmlzZShieV9sb3NpbmdfZ2FtZV9zY29yZSkKaGlzdChMb3NpbmdfR2FtZXNfZGlzdCRURUFNX1NDT1JFKQphYmxpbmUodiA9IG1lYW4oTG9zaW5nX0dhbWVzX2Rpc3QkVEVBTV9TQ09SRSksIGNvbCA9ICJibHVlIiwgbHdkID0gMikKYm94cGxvdChMb3NpbmdfR2FtZXNfZGlzdCRURUFNX1NDT1JFKQpzdW1tYXJ5KExvc2luZ19HYW1lc19kaXN0JFRFQU1fU0NPUkUpCmBgYAoKCjxoMj5BbmFseXNpczwvaDI+CgpOb3cgdGhhdCB0aGVyZSBpcyBhIGxpdHRsZSBiZXR0ZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGF0YSwgbGV0cyB0YWtlIGEgbG9vayBhdCBmaXZlIGFuYWx5dGljYWwgcXVlc3Rpb25zIGF0IGhhbmQ6CgoxLiBQbGF5ZXJzIHdpdGggdGhlIG1vc3QgcG9pbnRzIHNjb3JlZCBzaW5jZSAxOTk2LTk3IHNlYXNvbj8gCjIuIFBsYXllcnMgd2l0aCB0aGUgbW9zdCBnYW1lcyBvdmVyIDQwIHBvaW50cz8KMy4gV2hvIGhhcyB0aGUgYmVzdCBmaWVsZCBnb2FsIHJhdGlvcyBpbiB0aGVpciBjYXJlZXI/KiAgIAo0LiBQbGF5ZXIgd2l0aCB0aGUgYmVzdCBvdmVyYWxsIHN0YXRzIGluIHRoZWlyIGNhcmVlcj8qCjUuIFdoaWNoIHRlYW0gZGlkIExlQnJvbiBwbGF5IHRoZSBiZXN0IG9uPyoKCiogdGhlIHF1ZXN0aW9ucyBzZWxlY3RlZCBmb3IgdGhlIHZpc3VhbGl6YXRpb24KCgoKXG5ld3BhZ2UKPGgzPlF1ZXN0aW9uIDEgLSBQbGF5ZXJzIHdpdGggdGhlIG1vc3QgcG9pbnRzIHNjb3JlZCBzaW5jZSAxOTk2LTk3IHNlYXNvbj88L2gzPgoKYGBge3J9CmJ5X3BsYXllciA8LSBncm91cF9ieShnYW1lcyxQTEFZRVIpCgptb3N0X3BvaW50c19zY29yZWQgPC0gc3VtbWFyaXNlKGJ5X3BsYXllciwgcG9pbnRzID0gc3VtKFBUUykpICU+JQogIGFycmFuZ2UoZGVzYyhwb2ludHMpKSAlPiUKICB0b3Bfbig1KQoKbW9zdF9wb2ludHNfc2NvcmVkCmBgYAoKSGVyZSBpcyBhIGxpc3Qgb2YgdGhlIHRvcCA1IHBsYXllcnMgd2l0aCB0aGUgaGlnaGVzdCBwb2ludHMgc2NvcmVkIHNpbmNlIDE5OTYtOTcgc2Vhc29uLiAgCgoKXG5ld3BhZ2UKPGgzPlF1ZXN0aW9uIDIgLSBQbGF5ZXJzIHdpdGggdGhlIG1vc3QgZ2FtZXMgb3ZlciA0MCBwb2ludHM/PC9oMz4KYGBge3J9Cm1vc3RfZ2FtZXNfb3Zlcl80MHB0cyA8LSBncm91cF9ieShnYW1lcyxQTEFZRVIpICU+JQogIGZpbHRlcihQVFM+NDApICU+JQogIGNvdW50KFBMQVlFUikgJT4lCiAgYXJyYW5nZShkZXNjKG4pKSAlPiUKICB1bmdyb3VwKFBMQVlFUikgJT4lCiAgdG9wX24oNSkKICAKbW9zdF9nYW1lc19vdmVyXzQwcHRzCgpgYGAKCkhlcmUgaXMgYSBsaXN0IG9mIHRoZSB0b3AgNSBwbGF5ZXJzIHdpdGggdGhlIG1vc3QgZ2FtZXMgd2l0aCBhdGxlYXN0IDQwIHBvaW50cyBzaW5jZSAxOTk2LTk3IHNlYXNvbi4gCgpOb3cgdGhhdCB3ZSBoYXZlIGEgZmV3IG9mIHdoYXQgSSB3b3VsZCBjb25zaWRlciB0aGUgdG9wIHBsYXllcnMgaW4gdGhlIE5CQSwgYWNjb3JkaW5nIHRvIHRvdGFsIHBvaW50cyBzY29yZWQgYW5kIG1vc3QgZ2FtZXMgd2l0aCBhdGxlYXN0IDQwIHB0cy4gTGV0cyBhbmFseXplIGEgZmV3IHN0YXRzIGZyb20gdGhlc2UgaW5kaXZpZHVhbHMgdG8gdHJ5IGFuZCBoZWxwIG5hcnJvdyBkb3duIGFuZCBkZXRlcm1pbmUgd2hvIGlzIHRoZSBiZXN0IG9mIHRoZXNlIHBsYXllciBpbiB0aGUgTkJBIHNpbmNlIDE5OTYtOTcgc2Vhc29uLiAKCgpcbmV3cGFnZQo8aDM+UXVlc3Rpb24gMyAtIFdobyBoYXMgdGhlIGJlc3QgZmllbGQgZ29hbCByYXRpb3MgaW4gdGhlaXIgY2FyZWVyPyA8L2gzPgoKYGBge3J9CmZnciA8LSBtdXRhdGUoZ2FtZXMsIEZHUiA9IEZHL0ZHQSwgRkdSM1BUID0gRkczL0ZHM0EsIEZHUkZUID0gRlQvRlRBKSAlPiUKICBmaWx0ZXIoUExBWUVSID09IG1vc3RfZ2FtZXNfb3Zlcl80MHB0cyRQTEFZRVIgfCBQTEFZRVIgPT0gbW9zdF9wb2ludHNfc2NvcmVkJFBMQVlFUikKCmJlc3RfZmdyIDwtIGdyb3VwX2J5KGZncixQTEFZRVIpICU+JQogIHN1bW1hcmlzZShhdmdmZ3IgPSBtZWFuKEZHUiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShkZXNjKGF2Z2ZncikpCgpiZXN0XzNwdHIgPC0gZ3JvdXBfYnkoZmdyLFBMQVlFUikgJT4lCiAgc3VtbWFyaXNlKGF2ZzNwdHIgPSBtZWFuKEZHUjNQVCwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShkZXNjKGF2ZzNwdHIpKQoKYmVzdF9mdHIgPC0gZ3JvdXBfYnkoZmdyLFBMQVlFUikgJT4lCiAgc3VtbWFyaXNlKGF2Z2Z0ciA9IG1lYW4oRkdSRlQsIG5hLnJtID0gVFJVRSkpICU+JQogIGFycmFuZ2UoZGVzYyhhdmdmdHIpKQoKbWVyZ2VkX2ZnciA8LSBtZXJnZShiZXN0X2ZncixiZXN0XzNwdHIsYnkueD0iUExBWUVSIikgJT4lCiAgbWVyZ2UoYmVzdF9mdHIsYnkueD0iUExBWUVSIikKbWVyZ2VkX2ZncgoKYGBgCgpTZWUgVml6IDMuMSBiZWxvdy0gVGhpcyB2aXN1YWxpemF0aW9uIGlzIHJlcHJlc2VudGVkIGJ5IGEgYmFyIHBsb3QsIGluIGRlc2NlbmRpbmcgb3JkZXIuIFRoZSByZWFzb24gZm9yIHRoaXMgdHlwZSBvZiBjaGFydCBmb3IgdGhpcyBkYXRhIHdhcyB0byBzaG93IHRoZSAicmFuayIgb2YgdGhlc2UgZWlnaHQgKDgpIHBsYXllcnMuIExvb2tpbmcgYXQgYm90aCB0aGUgdGFibGUgYW5kIGNoYXJ0IHdpbGwgaGVscCB1bmRlcnN0YW5kIHRoZSBkYXRhIGJldHRlci4gVGhlIG90aGVyIHBhcnQgb2YgdGhpcyB2aXN1YWxpemF0aW9uIHRoYXQgaGVscHMgaXMgdGhhdCBpdCBpcyByYW5rZWQgaW4gZGVjc2VuZGluZyBvcmRlciB0byBnaXZlIHRoZSB2aWV3ZXIgbGVzcyBpdGVtcyB0byBsb29rIGF0IGFsbCBhdCBvbmNlLCBhcyBjb21wYXJpbmcgYmFyIGhlaWdodHMgY2FuIGJlIGRlY2VpdmluZyBzb21ldGltZXMuCgohW1ZpeiAzXzE6IEF2ZyBGaWVsZCBHb2FsIFJhdGlvIHRvcCA4IHBsYXllcnNdKFEzVjEuanBnKQoKU2VlIFZpeiAzLjIgYmVsb3cgLSBUaGlzIHZpc3VhbGl6YXRpb24gaXMgcmVwcmVzZW50ZWQgYnkgYSBsb2xsaXBvcCBjaGFydC4gVGhlIHJlYXNvbiBmb3IgdGhpcyB0eXBlIG9mIGNoYXJ0IGZvciB0aGlzIGRhdGEgd2FzIHRvIHNob3cgdGhlIHNhbWUgZGF0YSBmcm9tIGEgc29tZXdoYXQgZGlmZmVyZW50IHZpZXcgInJhbmsiIG9mIHRoZXNlIGVpZ2h0ICg4KSBwbGF5ZXJzLiBIb3dldmVyLCB0aGUgZGF0YSBiZWluZyByZXByZXNlbnRlZCBoZXJlIGRpZmZlcmVudCBhbmQgaXQgaXMgdXNpbmcgdGhlIGF2ZXJhZ2UgMyBwb2ludCByYXRpbyAoYXZnM3B0cikuIFNpbWlsYXJseSwgdGhlIGNoYXJ0IGlzIGluIGRlc2NlbmRpbmcgb3JkZXIgdG8gZ2l2ZSB0aGUgdmlld2VyIGxlc3MgaXRlbXMgdG8gbG9vayBhdCBhbGwgYXQgb25jZS4KCiFbVml6IDNfMjogQXZnIDNwdCBSYXRpbyB0b3AgOCBwbGF5ZXJzXShRM1YyLmpwZykKU2VlIFZpeiAzLjMgYmVsb3cgLSBUaGlzIHZpc3VhbGl6YXRpb24gaXMgcmVwcmVzZW50ZWQgYnkgYSBjaXJjbGUgcGxvdCB3aXRoIDMgYXhlcywgb25lIGZvciBlYWNoIGNvbHVtbi9mZWF0dXJlLiBUaGUgcmVhc29uIGZvciB0aGlzIHR5cGUgb2YgY2hhcnQgZm9yIHRoaXMgZGF0YSB3YXMgdG8gc2hvdyBhIGNvbXBhcmlzb24gYmV0d2VlbiBwbGF5ZXJzLiBGb3IgdGhlIHB1cnBvc2UgYW5kIHByb29mIG9mIGNvbmNlcHQsIHVzZWQgaXQgdG8gcmVwcmVzZW50IG9ubHkgMiBwbGF5ZXJzKExlYnJvbiBKYW1lcyBhbmQgS29iZSBCcnlhbnQpLiBUaGVyZSB3b3VsZCBoYXZlIGJlZW4gMjggY29tYmluYXRpb25zIGZvciBhbnkgMiBwbGF5ZXJzLCBhbmQgd291bGQgYmVzdCB3b3JrIHdpdGggYW4gaW50ZXJhY3RpdmUgdmlldyBvZiBzb21lIHNvcnQgdG8gc2VsZWN0IHBsYXllciAxIGFuZCBwbGF5ZXIgMiBmb3IgY29tcGFyaXNvbi4gVGhlIHB1cnBvc2Ugb2YgdGhpcyB2aXN1YWxpemF0aW9uIHdvdWxkIGJlIHRvIGVhc2lseSBiZSBhYmxlIHRvIGNvbXBhcmUgc3RhdHMgb2YgMiBwbGF5ZXJzIHZlcnN1cyBoYXZpbmcgdG8gbG9vayBhdCBtdWx0aXBsZSBjaGFydHMsIGFuZCBwaWVjaW5nIHRvZ2V0aGVyIGluZm9ybWF0aW9uLgoKIVtWaXogM18zOiBDb21wYXJpc29uIGJldHdlZW4gMiBwbGF5ZXJzXShRM1YzLmpwZykKRXh0cmE6IEluIGFkZGl0aW9uLCB0byB0aGUgY2FyZWVyIHN0YXRzLCBzb21ldGltZXMgaXRzIG1lYW5pbmdmdWwgdG8gbG9vayBhdCBhIGZldyBwZXIgeWVhciBzdGF0cyBhcyB3ZWxsLiBCZWxvdyBpcyB0aGUgRmllbGQgZ29hbCByYXRpbyBwZXIgeWVhciBmb3IgdGhlc2UgcGxheWVycywgYWxvbmcgd2l0aCB0aGVpciBwb2ludHMgcGVyIGdhbWUgcGVyIHllYXIgc3RhdHMuIFRoaXMgd2lsbCBoZWxwIHdpdGggbWFraW5nIHN1cmUgdGhlIHBsYXllcnMgdGhhdCBhcmUgc2NvcmluZyBoaWdoZXIgcG9pbnRzIGFyZSBub3QgZG9pbmcgc28gYXQgdGhlIHNhY3JpZmljZSBvZiB0aGVpciBmaWVsZCBnb2FsIHBlcmNlbnRhZ2UsIGFuZCBub3QgaGF2ZSBpdCBwbGF5IGludG8gdGhlIGlkZWEgb2YgbW9yZSBzaG90cyB0YWtlbiB0aGUgaGlnaGVyIHRoZSBzY29yZS4gCgpgYGB7cn0KYnlfcGxheWVyIDwtIGZpbHRlcihmZ3IpICU+JQogIGdyb3VwX2J5KFBMQVlFUiwgeWVhcikKCnBwZ19wZXJfeXIgPC0gc3VtbWFyaXNlKGJ5X3BsYXllciwgcG9pbnRzID0gbWVhbihQVFMsIG5hLnJtID0gVFJVRSkpICU+JQogIGFycmFuZ2UoeWVhcikKCnBwZ19wZXJfeXIKCiMgcGxvdApnZ3Bsb3QoZGF0YSA9IHBwZ19wZXJfeXIsIGFlcyh4PXBwZ19wZXJfeXIkeWVhciwgeT1wcGdfcGVyX3lyJHBvaW50cywgZ3JvdXAgPSBwcGdfcGVyX3lyJFBMQVlFUiwgY29sb3IgPXBwZ19wZXJfeXIkUExBWUVSKSkgKyBnZW9tX2xpbmUoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKCmBgYApBcyBzZWVuIGluIHRoaXMgdmlzdWFsaXphdGlvbiBhYm92ZSwgdGhlIHRvcCAzIHBsYXllcnMgb2YgdGhlIDggdGhhdCBhcmUgYmVpbmcgZXhhbWluZWQgYXJlIEphbWVzIEhhcmRlbiwgTGVicm9uIEphbWVzLCBhbmQgS2V2aW4gRHVyYW50IGluIHRlcm1zIG9mIG1vc3QgcG9pbnRzIHBlciBnYW1lIHBlciB5ZWFyLiAKCmBgYHtyfQpieV9wbGF5ZXIgPC0gZmlsdGVyKGZncikgJT4lCiAgZ3JvdXBfYnkoUExBWUVSLCB5ZWFyKQoKYmVzdF9mZ3JfcGVyX3lyIDwtIHN1bW1hcmlzZShieV9wbGF5ZXIsIEZHUl9wZXJfeXIgPSBtZWFuKEZHUiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZSh5ZWFyKQoKYmVzdF9mZ3JfcGVyX3lyCgojIHBsb3QKZ2dwbG90KGRhdGEgPSBiZXN0X2Zncl9wZXJfeXIsIGFlcyh4PWJlc3RfZmdyX3Blcl95ciR5ZWFyLCB5PWJlc3RfZmdyX3Blcl95ciRGR1JfcGVyX3lyLCBncm91cCA9IGJlc3RfZmdyX3Blcl95ciRQTEFZRVIsIGNvbG9yID1iZXN0X2Zncl9wZXJfeXIkUExBWUVSKSkgKyBnZW9tX2xpbmUoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKYGBgCk5vdyBpbiB0aGUgdmlzdWFsaXphaXRvbiBhYm92ZSwgbGV0cyB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgSmFtZXMgSGFyZGVuLCBMZWJyb24gSmFtZXMsIGFuZCBLZXZpbiBEdXJhbnQgaW4gdGVybXMgb2YgdGhlaXIgZmllbGQgZ29hbCBwZXJjZW50YWdlIHBlciB5ZWFyLiBOb3RpY2UgdGhhdCBKYW1lcyBIYXJkZW4gaGFzIGEgbG93ZXIgcmF0aW8gdGhhbiBib3RoIExlYnJvbiBKYW1lcyBhbmQgS2V2aW4gRHVyYW50LCBsZWFkaW5nIHVzIHRvIGJlbGlldmUgaGUgaGFzIGhpZ2hlciBwb2ludHMgcGVyIGdhbWUgb25seSBiZWNhdXNlIG9mIGhvdyBtYW55IHNob3RzIGhlIHRvb2sgaW4gdG90YWwuCgpcbmV3cGFnZQo8aDM+UXVlc3Rpb24gNCAtIFBsYXllciB3aXRoIHRoZSBiZXN0IG92ZXJhbGwgc3RhdHMgaW4gdGhlaXIgY2FyZWVyPyA8L2gzPgpgYGB7cn0Kc3RhdHMgPC0gZmlsdGVyKGZncikKCmJlc3Rfc3RhdHMgPC0gZ3JvdXBfYnkoc3RhdHMsUExBWUVSKSAlPiUKICBzdW1tYXJpc2UoYXZnVFJCID0gbWVhbihUUkIsIG5hLnJtID0gVFJVRSksIGF2Z0FTVCA9IG1lYW4oQVNULCBuYS5ybSA9IFRSVUUpLCBhdmdTVEwgPSBtZWFuKFNUTCwgbmEucm0gPSBUUlVFKSwgYXZnQkxLID0gbWVhbihCTEssIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZSh0b3RhbCA9IGF2Z1RSQithdmdBU1QrYXZnU1RMK2F2Z0JMSykgJT4lCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkKCmJlc3Rfc3RhdHMKCmJ5X3BsYXllciA8LSBmaWx0ZXIoZmdyKSAlPiUKICBncm91cF9ieShQTEFZRVIsIHllYXIpCgpiZXN0X3N0YXRzX3Blcl95ZWFyIDwtIHN1bW1hcmlzZShieV9wbGF5ZXIsIGF2Z1RSQiA9IG1lYW4oVFJCLCBuYS5ybSA9IFRSVUUpLCBhdmdBU1QgPSBtZWFuKEFTVCwgbmEucm0gPSBUUlVFKSwgYXZnU1RMID0gbWVhbihTVEwsIG5hLnJtID0gVFJVRSksIGF2Z0JMSyA9IG1lYW4oQkxLLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUodG90YWwgPSBhdmdUUkIrYXZnQVNUK2F2Z1NUTCthdmdCTEspICU+JQogIGFycmFuZ2UoZGVzYyh0b3RhbCkpCgpiZXN0X3N0YXRzX3Blcl95ZWFyCgpnZ3Bsb3QoZGF0YSA9IGJlc3Rfc3RhdHNfcGVyX3llYXIsIGFlcyh4PWJlc3Rfc3RhdHNfcGVyX3llYXIkeWVhciwgeT1iZXN0X3N0YXRzX3Blcl95ZWFyJHRvdGFsLCBncm91cCA9IGJlc3Rfc3RhdHNfcGVyX3llYXIkUExBWUVSLCBjb2xvciA9YmVzdF9zdGF0c19wZXJfeWVhciRQTEFZRVIpKSArIGdlb21fbGluZSgpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQoKYGBgClNlZSBWaXogNC4xIGFib3ZlLSBUaGlzIHZpc3VhbGl6YXRpb24gaXMgcmVwcmVzZW50ZWQgYnkgYSBtdWx0aXBsZSBsaW5lIHBsb3QuIFRoaXMgdmlzdWFsaXphdGlvbiBhbGxvd3MgdGhlIHVzZXIgdG8gdmlldyAzIHZhcmlhYmxlcyBhdCB0aGUgc2FtZSB0aW1lLiBUaGUgdmlzdWFsaXphdGlvbiBpcyBhYmxlIHRvIHNob3cgdGhlIHRvdGFsIHN0YXRzKHkgYXhpcyksIHRoZSB5ZWFyKHggYXhpcyksIGFuZCB0aGUgcGxheWVyIChjb2xvcmVkIGxpbmUpLlRoZSB5IGF4aXMgaXMgY2FsY3VsYXRlZCBieSBhZGRpbmcgdG9nZXRoZXIgdGhlIGF2ZXJhZ2UgdG90YWwgcmVib3VuZHMgcGVyIGdhbWUsIGF2ZXJhZ2UgYXNzaXN0IHBlciBnYW1lLCBhdmVyYWdlIHN0ZWFsIHBlciBnYW1lLCBhbmQgYXZlcmFnZSBibG9jayBwZXIgZ2FtZSAtIGFsbCBmb3IgYSBnaXZlbiB5ZWFyLiBUaGlzIGlzIGFsc28ganVzdCBhIHNpbXBsaWZpY2F0aW9uIG9mIHRoZSBzdGF0cywgYXMgaW4gdGhlIHJlYWwgZ2FtZSB0aGVyZSBhcmUgd2VpZ2h0cyBhc3NvY2lhdGVkIHdpdGggdGhlc2Ugc3RhdHMsIHdoZXJlIHJlYm91bmRzIGFuZCBhc3Npc3RzIGFyZSBtb3JlIGhlYXZpbHkgd2VpZ2hlZCB0aGFuIGJsb2NrcyBhbmQgc3RlYWxzLiAKCgpTZWUgVml6IDQuMiBiZWxvdy0gVGhpcyB2aXN1YWxpemF0aW9uIGlzIHJlcHJlc2VudGVkIGJ5IGEgbXVsdGlwbGUgbWFya3MuIFRoaXMgdmlzdWFsaXphdGlvbiBhbHNvIGFsbG93cyB0aGUgdXNlciB0byB2aWV3IDMgdmFyaWFibGVzIGF0IHRoZSBzYW1lIHRpbWUuIEhvd2V2ZXIsIGluIHRoaXMgdmlzdWFsaXphdGlvbiBpdCBzaG93cyB0aGUgc2VwYXJhdGUgc3RhdHMgcGVyIHBlcnNvbiBhbmQgbm90IHBlciB5ZWFyLiBUaGUgYXZlcmFnZSBzdGF0IHNjb3JlIGlzIG9uIHRoZSB5IGF4aXMsIHRoZSBzdGF0IG9uIHRoZSB4IGF4aXMsIGFuZCBlYWNoIHBsYXllciBpcyByZXByZXNlbnRlZCBieSBhIGRpZmZlcmVudCBtYXJrLlRoZSB5IGF4aXMgcmVwcmVzZW50IHRoZSBhdmVyYWdlIHRvdGFsIHJlYm91bmRzIHBlciBnYW1lLCBhdmVyYWdlIGFzc2lzdCBwZXIgZ2FtZSwgYXZlcmFnZSBzdGVhbCBwZXIgZ2FtZSwgYW5kIGF2ZXJhZ2UgYmxvY2sgcGVyIGdhbWUgLSBpbiBhIHBsYXllcnMgd2hvbGUgY2FyZWVyLiAKCiFbVml6IDRfMjogU3RhdHMgRG90IFBsb3Qgb2YgdGhlIHRvcCA4IHBsYXllcnNdKFE0VjIuanBnKQoKU2VlIFZpeiA0LjMgYmVsb3ctIFRoaXMgdmlzdWFsaXphdGlvbiBpcyByZXByZXNlbnRlZCBieSBzdGFja2VkIGJhciBjaGFydC4gVGhpcyB2aXN1YWxpemF0aW9uIGFsc28gYWxsb3dzIHRoZSB1c2VyIHRvIHZpZXcgbXVsdGlwbGUgdmFyaWFibGVzIGF0IG9uY2UgYXMgd2VsbC4gSW4gdGhpcyB2aXN1YWxpemF0aW9uLCB0aGUgc3RhY2tlZCBiYXIgaXMgYWJsZSB0byBzaG93IHRoZSBicmVha2Rvd24gb2YgdGhlIHRvdGFsIHN0YXQuIFRoZSB0b3RhbCBzdGF0IHNjb3JlIGlzIG9uIHRoZSB5IGF4aXMsIHRoZSBwbGF5ZXIgaXMgb24gdGhlIHggYXhpcywgYW5kIGVhY2ggc3RhdCBpcyByZXByZXNlbnRlZCBieSBhIGRpZmZlcmVudCBzaGFkaW5nL3RleHR1cmUuVGhlc2UgYXJlIGFsc28gcmVwcmVzZW50aW5nIHdob2VsIGNhcmVlciBzdGF0cyB2ZXJzdXMgdGhlIHllYXJseSBzdGF0cyBzZWVuIGluIFZpeiA0XzEuIAoKIVtWaXogNF8zOiBTdGFja2VkIGJhciBjaGFydCBvZiB0aGUgdG9wIDggcGxheWVyc10oUTRWMy5qcGcpCgpUbyBjb250aW51ZSB0aGUgY29udmVyc2F0aW9uIG9mIHdobyBpcyBiZXN0LCBpbml0aWFsbHkgaW4gVml6IDRfMSBpdCBjYW4gYmUgc2VlbiB0aGF0IERpcmsgTm93aXR6a2kgYW5kIFRpbSBEdW5jYW4gd2VyZSBvbiB0b3AsIHVudGlsIExlYnJvbiBKYW1lcyBzdGFydGVkIGhpcyBjYXJlZXIgaW4gMjAwMywgYW5kIHRoZXkgc3RhcnRlZCBvbiB3aGF0IGxvb2tzIHRvIGJlIGEgc3RlYWR5IGRlY2xpbmUgdW50aWwgcmV0aXJlbWVudC4gTGVicm9uIEphbWVzIHNpbmNlIHN0YXJ0aW5nIGluIDIwMDMsIGhhZCAxMiBvdXQgb2YgMTggeWVhcnMgb2YgYmVpbmcgb24gdGhlIHRvcCBvZiB0aGlzIGNoYXJ0LCBhbmQgdGhlIG90aGVyIDYgeWVhcnMgaGUgd2FzIHNlY29uZCBvbiB0aGlzIGNoYXJ0LiBMZWJyb24gSmFtZXMgaGFzIHRoZSBoaWdoZXN0IG92ZXJhbGwgc3RhdCB0b3RhbCBmcm9tIHRoZSA4IHBsYXllcnMgc2VlbiBpbiBWaXogNF8zLgoKU2FmZSB0byBzYXkgdGhhdCBzdGF0aXN0aWNhbGx5LCBMZWJyb24gSmFtZXMgaXMgdGhlIGJlc3Qgb3ZlcmFsbCBwbGF5ZXIgaW4gdGhlIE5CQSBhY2NvcmRpbmcgdG8gdGhpcyBkYXRhc2V0IHRoYXQgY292ZXJzIGFsbCBnYW1lcyBmcm9tIDE5OTYtOTcgc2Vhc29uIHRvIERlY2VtYmVyIDMxLCAyMDIwLgoKXG5ld3BhZ2UKPGgzPlF1ZXN0aW9uIDUgLSBXaGljaCB0ZWFtIGRpZCBMZUJyb24gcGxheSB0aGUgYmVzdCBvbj8gPC9oMz4KYGBge3J9CmJ5X3BsYXllciA8LSBmaWx0ZXIoZ2FtZXMsIFBMQVlFUj09IkxlQnJvbiBKYW1lcyIpICU+JQogIGdyb3VwX2J5KFRFQU0seWVhcikKCkxlYnJvbl9tb3N0X3BvaW50c19wZXJfeWVhciA8LSBzdW1tYXJpc2UoYnlfcGxheWVyLCBwb2ludHMgPSBzdW0oUFRTKSkgJT4lCiAgYXJyYW5nZSh5ZWFyKQogCkxlYnJvbl9tb3N0X3BvaW50c19wZXJfeWVhcgoKIyBwbG90CmdncGxvdChkYXRhID0gTGVicm9uX21vc3RfcG9pbnRzX3Blcl95ZWFyLCBhZXMoeD1MZWJyb25fbW9zdF9wb2ludHNfcGVyX3llYXIkVEVBTSwgeT1MZWJyb25fbW9zdF9wb2ludHNfcGVyX3llYXIkcG9pbnRzLCBncm91cCA9IExlYnJvbl9tb3N0X3BvaW50c19wZXJfeWVhciRURUFNLCBjb2xvcj1MZWJyb25fbW9zdF9wb2ludHNfcGVyX3llYXIkVEVBTSkpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgpgYGAKU2VlIFZpeiA1LjEgYWJvdmUtIFRoaXMgdmlzdWFsaXphdGlvbiBpcyByZXByZXNlbnRlZCBieSBhIG11bHRpcGxlIGJveCBwbG90cy4gVGhpcyB2aXN1YWxpemF0aW9uIGFsbG93cyB0aGUgc3ByZWFkIG9mIHNjb3Jlcy4gVGhlIHZpc3VhbGl6YXRpb24gaXMgYWJsZSB0byBzaG93IHRoZSB0b3RhbCBwb2ludHMgcGVyIHllYXIoeSBheGlzKSwgdGhlIHRlYW0oeCBheGlzKS4gVGhpcyBjaGFydCB3YXMgY2hvc2VuIHRvIG5vdCBvbmx5IHNob3cgdGhlIExlYnJvbidzIGF2ZXJhZ2UgcG9pbnRzIHNjb3JlZCBwZXIgeWVhciwgYnV0IHRvIHNob3cgdGhlIHJhbmdlIG9mIHNjb3JlcyBoZSBoYXMgc2NvcmVkIGZvciBhIHBhcnRpY3VsYXIgdGVhbS4gTm90aWNlIHRoYXQgdGhlIGJveCBwbG90IGZvciBMYWtlcnMgaXMgc21hbGwsIHRoYXQgaXMgdWx0aW1hdGVseSBkdWUgdG8gdGhlIGxpbWl0ZWQgbnVtYmVyIG9mIHllYXJzIGhlIGhhcyBiZWVuIHdpdGggdGhhdCBmcmFuY2hpc2UuIE9uIGF2ZXJhZ2UsIExlYnJvbiBoYWQgdGhlIG1vc3QgcG9pbnRzIHNjb3JlZCBwZXIgeWVhciBvbiB0aGUgQ2xldmVsYW5kIENhdmFsaWVycy4KCmBgYHtyfQoKTGVicm9uX2F2Z19wb2ludHNfcGVyX3llYXIgPC1zdW1tYXJpc2UoYnlfcGxheWVyLCBhdmdNaW5zUGxheWVkID0gbWVhbihNUCwgbmEucm09VFJVRSksIHBvaW50cyA9IG1lYW4oUFRTLCBuYS5ybT1UUlVFKSkgJT4lCiAgYXJyYW5nZSh5ZWFyKQoKTGVicm9uX2F2Z19wb2ludHNfcGVyX3llYXIKCiMgcGxvdApnZ3Bsb3QoZGF0YSA9IExlYnJvbl9hdmdfcG9pbnRzX3Blcl95ZWFyLCBhZXMoeD1MZWJyb25fYXZnX3BvaW50c19wZXJfeWVhciR5ZWFyLCB5PUxlYnJvbl9hdmdfcG9pbnRzX3Blcl95ZWFyJHBvaW50cywgZ3JvdXAgPSBMZWJyb25fYXZnX3BvaW50c19wZXJfeWVhciRURUFNLCBjb2xvcj1MZWJyb25fYXZnX3BvaW50c19wZXJfeWVhciRURUFNKSkgKyBnZW9tX3BvaW50KCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgpgYGAKClNlZSBWaXogNS4yIGFib3ZlLSBUaGlzIHZpc3VhbGl6YXRpb24gaXMgcmVwcmVzZW50ZWQgYnkgYSBzY2F0dGVyIHBsb3QuIFRoaXMgdmlzdWFsaXphdGlvbiBhbGxvd3MgdG8gc3ByZWFkIG9mIHNjb3JlcyBhY3Jvc3MgbXVsdGlwbGUgeWVhcnMuIFRoZSB2aXN1YWxpemF0aW9uIGlzIGFibGUgdG8gc2hvdyB0aGUgYXZlcmFnZSBwb2ludHMgcGVyIGdhbWUgcGVyIHllYXIoeSBheGlzKSwgdGhlIHllYXIoeCBheGlzKS4gVGhpcyBjaGFydCB3YXMgY2hvc2VuIHRvIHRvIHNob3cgaG93IExlYnJvbiBzY29yZWQgb24gYXZlcmFnZSBpbiBhbnkgZ2l2ZW4geWVhci4gTm90aWNlIHRoYXQgb25seSBoaXMgZmlyc3QgMiB5ZWFycyBpbiB0aGUgTkJBIGRpZCBoZSBoYXZlIGEgMjIuNSBhbmQgbG93ZXIgYXZlcmFnZSBnYW1lIHNjb3JlICh3aGljaCBpcyBzdGlsbCB2ZXJ5IGltcHJlc3NpdmUpLiBBbHNvIHRoYXQgaGlzIHRvcCBzZWFzb25zIHdlcmUgd2l0aCB0aGUgQ2xldmVsYW5kIENhdmFsaWVycy4gCgoKYGBge3J9CkxlYnJvbl90ZWFtX3N1bW1hcnkgPC0gZmlsdGVyKGdhbWVzLCBQTEFZRVI9PSJMZUJyb24gSmFtZXMiKSAlPiUKICBncm91cF9ieShURUFNKSAlPiUKICBjb3VudCgpCgpMZWJyb25fdGVhbV9zdW1tYXJ5CgpMZUJyb25fUG9pbnRzX1N1bW1hcnkgPC0gdW5ncm91cChMZWJyb25fbW9zdF9wb2ludHNfcGVyX3llYXIpICU+JQogIGdyb3VwX2J5KFRFQU0pICU+JQogIHN1bW1hcml6ZShUb3RhbFB0cyA9IHN1bShwb2ludHMpKQoKTGVCcm9uX1BvaW50c19TdW1tYXJ5CiAgCmBgYApTZWUgVml6IDUuMyBiZWxvdy0gVGhpcyB2aXN1YWxpemF0aW9uIGlzIHJlcHJlc2VudGVkIGJ5IHBpZSBjaGFydC4gVGhpcyB2aXN1YWxpemF0aW9uIGFsc28gYWxsb3dzIHRoZSB1c2VyIHRvIHZpZXcgcmVsYXRpdmUgc2hhcmUgb2YgZ2FtZXMgcGxheWVkIGFuZCB0b3RhbCBwb2ludHMgc2NvcmVkIHdpdGggYSBjZXJ0YWluIHRlYW0uIE5vdGljZSBoZXJlIHRoYXQgdGhlIGdhbWVzIHBsYXllZCBzaGFyZSBhbmQgdGhlIHBvaW50cyBzY29yZWQgZm9yIExlYnJvbiBhcmUgdmVyeSBoaWdobHkgY29ycmVsYXRlZC4gd2hpY2ggbWVhbnMgaGUgaGFzIGJlZW4gcmVhbGx5IGNvbnNpc3RlbnQgZHVyaW5nIGhpcyB0aW1lIHdpdGggZWFjaCB0ZWFtLiAKCiFbVml6IDVfMzogTGVCcm9uIEphbWVzIG92ZXJhbGwgR2FtZSBTdW1tYXJ5XShRNVYzLmpwZykKCkluIGNvbmNsdXNpb24gdG8gdGhpcyBxdWVzdGlvbiwgdGhlIHRlYW0gdGhhdCBoZSBwZXJmb3JtZWQgaGlzIGJlc3Qgc28gZmFyIGlzIHdpdGggdGhlIENsZXZlbGFuZCBDYXZhbGllcnMuIAo=